#pragma once
#include "noncopyable.h"
#include "InetAddress.h"
#include "Callback.h"
#include "Buffer.h"

#include <memory>
#include <atomic>
#include <string>

class Channel;
class EventLoop;
class Socket;

class TcpConnection : noncopyable, public std::enable_shared_from_this<TcpConnection>
{
public:
  TcpConnection(EventLoop* loop,
                const std::string& name,
                int sockfd,
                const InetAddress& localAddr,
                const InetAddress& peerAddr);
  ~TcpConnection();
  EventLoop* getLoop() const { return m_loop; }
  const std::string& name() const { return m_name; }
  const InetAddress& localAddress() const { return m_localAddr; }
  const InetAddress& peerAddress() const { return m_peerAddr; }
  bool connected() const { return m_state == kConnected; }
  void send(const void* message, int len);
  void shutdown();
  void setConnectionCallback(const ConnectionCallback& cb) { m_connectionCallback = std::move(cb); }
  void setMessageCallback(const MessageCallback& cb) { m_messageCallback = std::move(cb); }
  void setWriteCompleteCallback(const WriteCompleteCallback& cb) { m_writeCompleteCallback = std::move(cb); }
  void setCloseCallback(const CloseCallback& cb) { m_closeCallback = std::move(cb); }
  void setHighWaterMarkCallback(const HighWaterMarkCallback& cb) { m_highWaterMarkCallback = std::move(cb); }
  void connectEstablished();
  void connectDestroyed();
  void send(const std::string& message);
private:
  enum StateE {
    kConnecting,
    kConnected,
    kDisconnecting,
    kDisconnected
  };
  void handleRead(Timestamp receiveTime);
  void handleWrite();
  void handleClose();
  void handleError();
  void sendMessageInLoop(const void* message, int len);
  void shutdownInLoop();
  void setState(const StateE state) { m_state = state; }
private:
  EventLoop *m_loop;
  std::atomic<StateE> m_state;
  bool m_reading;
  std::unique_ptr<Socket> m_socket;
  std::unique_ptr<Channel> m_channel;
  const InetAddress m_localAddr;
  const InetAddress m_peerAddr;

  ConnectionCallback m_connectionCallback;
  MessageCallback m_messageCallback;
  WriteCompleteCallback m_writeCompleteCallback;
  CloseCallback m_closeCallback;
  HighWaterMarkCallback m_highWaterMarkCallback;

  size_t m_highWaterMark;
  Buffer m_inputBuffer;
  Buffer m_outputBuffer;

  std::string m_name;
};